/*
 * ParseFct.java
 * 
 * Copyright 2004 Christoph Csallner and Yannis Smaragdakis.
 */
package edu.gatech.cc.cnc;

import java.util.Collection;
import java.util.Map;

import de.fhg.first.fd.X;
import edu.gatech.cc.cnc.params.Param;
import edu.gatech.cc.cnc.params.ParamArray;
import edu.gatech.cc.cnc.params.ParamBoolean;
import edu.gatech.cc.cnc.params.ParamByte;
import edu.gatech.cc.cnc.params.ParamComplex;
import edu.gatech.cc.cnc.params.ParamInt;
import edu.gatech.cc.cnc.params.ParamIntegral;
import edu.gatech.cc.cnc.params.ParamLong;
import edu.gatech.cc.cnc.params.ParamShort;
import edu.gatech.cc.cnc.params.ParamSimple;

/**
 * Functionality shared by the parsing passes.
 * 
 * @author csallner@gatech.edu (Christoph Csallner)
 */
public abstract class ParseFct {

	/**
	 * Result of parsing a ESC/Java constraint
	 */
  public static abstract class Result {
  	//TODO: Empty abstract class?
  }
  
  /** Function application. */
  protected static class F2App extends Result {
  	protected int r2;
  	protected X lhs = null;
  	protected X rhs = null;
  	protected ParamIntegral constParser = null;  //use only for parsing constants.
  	
  	public F2App(X l, int rel, X r, ParamIntegral p) {
  		assert l!=null && r!=null && p!=null;
  		constParser = p;
  		r2 = rel;
  		lhs = l;
  		rhs = r;
  	}

  	protected X parse(String c) {
  		return constParser.parse(c);
  	}
  	
  	protected ParamIntegral getConstParser() {return constParser;}
  	
  	public X apply() {
  		//Out.debug(lhs +" "+ rel(r2) +" " +rhs);
  		switch (r2) {
			case sym.STAR: 	return lhs.times(rhs);
			case sym.PLUS: 	return lhs.plus(rhs);
			case sym.MINUS:	return lhs.minus(rhs);
			case sym.SLASH:	return lhs.divby(rhs);
			default:
				throw new IllegalStateException("Unsupported function.");
			}
  	}
  }
  
  
  protected static int cnt = 1;  //currently parsed constraint
  protected Map<String,Param> params = null;
   
  
  
  /**
   * Constructor
   */
	public ParseFct (Map<String,Param> paramMapping) {
		params = paramMapping;
		cnt = 1;  //reset static counter
	}
	
	
  public static int getCnt() {return cnt;}
  public void incCnt() {cnt+=1;} 


  /**
	 * Add (name --> param) to list of params.
	 * Only place that adds a new entry.
	 */
	protected void addParam(String name, Param param) {
		assert name!=null;
		assert param!=null;
		
		/* Make sure that long name is used */
		
		params.put(name, param);
	}
	

	
	/** 
	 * @param varName o:26.19 | f:19.7 | RES-28.11:28.11
	 * @return ::= Param | String | null; 
	 */ 
	public Object var(String varName) {
		if (varName==null) {return null;}  //We are not interested in null varName.

		Param p = params.get(varName);
		if (p==null) {return varName;}  //not yet inserted---return varName String!
		
		return p;
	}
	

	/**
	 * @returns a new param wrapper for a given type.
	 */
	protected <T> Param<T> createParam(Class<T> type, String name) {
		if (type.equals(Integer.TYPE))
      return (Param<T>) new ParamInt(name);    
		if (type.equals(Boolean.TYPE))
      return (Param<T>) new ParamBoolean(name);    
		if (type.equals(Long.TYPE))
      return (Param<T>) new ParamLong(name);
		if (type.equals(Short.TYPE))
      return (Param<T>) new ParamShort(name);    
		if (type.equals(Byte.TYPE))
      return (Param<T>) new ParamByte(name);    
		if (type.isPrimitive())
      return (Param<T>) new ParamSimple<T>(name, type);
    
		if (type.isArray())
      return new ParamArray<T>(type, name);
    
    /* Non-array complex (including String) */
    
		return new ParamComplex<T>(type, name);
	}
	
	
	protected static String stripBarFromType(String name) {
		if (name.startsWith("|"))
			return name.substring(3, name.length()-1);  // |T_bla|
		
		return name.substring(2, name.length());  // T_bla
	}
	
	
	/**
	 * Strip variable name from ESC postfixes.
	 */
	public static String stripEscPostfix(String fNameEsc) {
		int escPos = fNameEsc.replace('@', ':').replace('-', ':').indexOf(':');
		if (escPos<0) {
			escPos = fNameEsc.indexOf('<');  //LS<1>
		}
		return fNameEsc.substring(0, escPos);
	}
	
	
	protected static String fileName2className(String fileName) {
		fileName = fileName.substring(0, fileName.lastIndexOf('.'));
		/* TODO bad hack:
		 *   There could be more than one class per file. */
		return fileName.replace('\\','/').replace('/','.');
		//return fileName.replace(System.getProperty("file.separator").charAt(0), '.');
	}
	
	/**
	 * Add each element from a to b that is a ParamIntegral.
	 * @returns modifies bList.
	 */
	protected <A extends Object>
	void addParamIntegrals(Collection<A> aList, Collection<ParamIntegral> bList) {
		for (A elem: aList) {
			if (elem instanceof ParamIntegral) {bList.add((ParamIntegral) elem);}				
		}
	}
}
